0%

kubernetes pod 调度

pod 是 kubernetes 中最小运行单元,默认情况下 pod 可以运行在所有工作节点上。但是默认的调度策略不能满足额外的需求,比如希望 pod 只调度某些节点;在副本数大于 1 时希望多个 pod 不要在同一个工作节点上;希望 pod 可以均匀的分布到多个工作节点等。

kubernetes pod 调度

kubernetes 默认调度器

kube-scheduler 是 kubernetes 集群中默认的调度器,在安装好 kubernetes 集群之后即可在 kube-system 命名空间下找到

kube-scheduler 发现需要创建一个 pod 时,会选择一个最佳的节点来运行新创建或者没有调度的 pod。每个 pod 有不同的要求,因此 kube-scheduler 会过滤掉不满足要求的节点。在一个集群中,可能会出现多个节点都满足要求,这些节点被称为可调度节点。如果一个集群中没有找到任何一个节点能满足 pod 的要求,那么这个 pod 会一直停留在未调度状态知道调度器可以找到合适的节点。

官方文档

绕过调度器的属性: nodeName

可以通过指定 pod 资源中的 spec.nodeName 名称来绕过调度器的节点选择。

通过设置 nodeName 来指定部署的节点是很方便的,但是使用这种方式存在以下问题:

  • 如果指定的节点不存在,那么 pod 不会创建
  • 如果指定的节点资源不满足要求,那么 pod 不会创建。比如一个节点规格为 16 核 32G 内存,但是 pod 的 resouces.request 设置的是 20 核 30G,那么这种情况下无法创建
  • 如果指定的节点掉线或者修改名称,这个情况下部署也是不稳定的

pod nodeName

根据节点标签进行选择: nodeSelector

有的时候 pod 执行需要特定资源,比如节点上有 GPU 或者 SSD,希望 pod 可以部署到拥有这些资源的节点上。那么这时可以使用 nodeSelector。

1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
nodeSelector:
disktype: ssd # 该 pod 需要运行在标签为 disktyp=ssd 的节点上

为了使用 nodeSelector,需要为节点加上 label

1
2
3
kubectl get nodes --show-labels # 查看所有节点和对应的 label
kubectl label node <nodeName> foo=bar # 为节点设置 label foo=bar
kubectl label node <nodeName> foo- # 取消节点的 foo label

如果 nodeSelector 指定了多个 label,那么部署的节点必须同时拥有这些 label

pod nodeSelector

节点亲和性: nodeAffinity

节点亲和性功能比 nodeSelector 更强,它既可以像 nodeSelector 一样只有在满足一定标签的情况下才能调度,也可以标明一些规则不是必须,如果没有满足这些条件任然可以调度节点

api 文档

节点亲和性由两种类型组成

  • requiredDuringSchedulingIgnoredDuringExecution: 一种强硬的规则,只有在满足定义的条件时才能进行调度
  • preferredDuringSchedulingIgnoredDuringExecution: 调度器尝试寻找满足条件的节点,如果没找到,任然会调度节点

requiredDuringSchedulingIgnoredDuringExecution

一个 requiredDuringSchedulingIgnoredDuringExecution 示例如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values:
- antarctica-east1
- antarctica-west1
- matchFields:
- key: metadata.name
operator: In
values:
- antarctica-east1
- antarctica-west1
containers:
- name: with-node-affinity
image: registry.k8s.io/pause:2.0

requiredDuringSchedulingIgnoredDuringExecution 只有一个字段 nodeSelectorTerms,其下可以跟多个数组,每个数组可以是 matchExpressionsmatchFields 两种类型。

matchExpressions 使用 label 进行筛选,matchFields 则是使用字段进行筛选。使用字段进行筛选参考 字段选择器

operator 一共有 6 种合法值,分别是 In, NotIn, Exists, DoesNotExist, Gt 和 Lt。 NodeSelectorRequirement。具体使用可以参考 操作符

preferredDuringSchedulingIgnoredDuringExecution

一个简单的 preferredDuringSchedulingIgnoredDuringExecution 示例如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 10
preference:
matchFields:
- key: metadata.name
operator: In
values:
- antarctica-east1
- antarctica-west1
- weight: 50
preference:
matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values:
- antarctica-east1
- antarctica-west1
containers:
- name: with-node-affinity
image: registry.k8s.io/pause:2.0

preferredDuringSchedulingIgnoredDuringExecution 的一个重要字段是 weight,这是一个必填字段,值在 1 到 100 之间。

当调度器找到能够满足 Pod 的其他调度请求的节点时,调度器会遍历节点满足的所有的偏好性规则, 并将对应表达式的 weight 值加和。

最终的加和值会添加到该节点的其他优先级函数的评分之上。 在调度器为 Pod 作出调度决定时,总分最高的节点的优先级也最高。

Pod 间亲和性与反亲和性

不管是 nodeName,nodeSelector 还是节点亲和性都只能选择 Pod 在哪个节点或者哪类节点上。但是如果一个 Deployoment 在有多个副本时希望尽可能让每个副本可以均匀的分布在多个可调度节点上时,以上字段就不能满足要求。此时可以考虑 Pod 间亲和性与反亲和性。